﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BoneImp {
	abstract class CurveTimeline : Timeline {
		float[] curves; /* dfx, dfy, ddfx, ddfy, dddfx, dddfy, ... */

		const float CURVE_LINEAR = 0;
		const float CURVE_STEPPED = -1;
		const int CURVE_SEGMENTS = 10;

		public CurveTimeline(int frameCount) : base(frameCount) {
			this.curves = new float[(frameCount - 1) * 6];
		}

		public void setLinear(int frameIndex) {
			this.curves[frameIndex * 6] = CURVE_LINEAR;
		}
		public void setStepped(int frameIndex) {
			this.curves[frameIndex * 6] = CURVE_STEPPED;
		}

		/* Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next.
		 * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
		 * the difference between the keyframe's values. */
		public void setCurve(int frameIndex, float cx1, float cy1, float cx2, float cy2) {
			float subdiv_step = 1.0f / CURVE_SEGMENTS;
			float subdiv_step2 = subdiv_step * subdiv_step;
			float subdiv_step3 = subdiv_step2 * subdiv_step;
			float pre1 = 3 * subdiv_step;
			float pre2 = 3 * subdiv_step2;
			float pre4 = 6 * subdiv_step2;
			float pre5 = 6 * subdiv_step3;
			float tmp1x = -cx1 * 2 + cx2;
			float tmp1y = -cy1 * 2 + cy2;
			float tmp2x = (cx1 - cx2) * 3 + 1;
			float tmp2y = (cy1 - cy2) * 3 + 1;
			int i = frameIndex * 6;
			this.curves[i] = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3;
			this.curves[i + 1] = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3;
			this.curves[i + 2] = tmp1x * pre4 + tmp2x * pre5;
			this.curves[i + 3] = tmp1y * pre4 + tmp2y * pre5;
			this.curves[i + 4] = tmp2x * pre5;
			this.curves[i + 5] = tmp2y * pre5;
		}

		protected float getCurvePercent (int frameIndex, float percent) {
			int curveIndex = frameIndex * 6;
			float dfx = this.curves[curveIndex];
			if (dfx == CURVE_LINEAR) return percent;
			if (dfx == CURVE_STEPPED) return 0;
			float dfy = this.curves[curveIndex + 1];
			float ddfx = this.curves[curveIndex + 2];
			float ddfy = this.curves[curveIndex + 3];
			float dddfx = this.curves[curveIndex + 4];
			float dddfy = this.curves[curveIndex + 5];
			float x = dfx, y = dfy;
			int i = CURVE_SEGMENTS - 2;
			while (true) {
				if (x >= percent) {
					float lastX = x - dfx;
					float lastY = y - dfy;
					return lastY + (y - lastY) * (percent - lastX) / (x - lastX);
				}
				if (i == 0) break;
				i--;
				dfx += ddfx;
				dfy += ddfy;
				ddfx += dddfx;
				ddfy += dddfy;
				x += dfx;
				y += dfy;
			}
			return y + (1 - y) * (percent - x) / (1 - x); /* Last point is 1,1. */
		}
	}
}
